import Vec3 from "./vec3";
import Quat from "./quat";
import Mat3 from "./mat3";
import Transform from "./transform";
import Aabb from "./aabb";

const DEFAULT_33=[1,0,0,0,1,0,0,0,1];
let tf0=new Float64Array(16);
let tv0=new Float64Array(4);

export default class Method {
    constructor(){}

    static absArray(a,out,aStart=0,outStart=0,length=3,fac=1){
        for(let i=0;i<length;i++){
            out[outStart+i]=Math.abs(a[aStart+i])*fac;
        }
        return out;
    }
    static addArray(a,b,out,startA=0,startB=0,startOut=0,length=3){
        let sa=startA||0;
        let sb=startB||0;
        let so=startOut||0;
        let len=length||Math.min(a.length,b.length);
        for(let i=0;i<len;i++){
            out[i+so]=a[i+sa]+b[i+sb];
        }
        return out;
    }
    static boxIntersectsBox(b0,b1){
        return b0[0] < b1[3] && b0[3] > b1[0] &&
            b0[1] < b1[4] && b0[4] > b1[1] &&
            b0[2] < b1[5] && b0[5] > b1[2];
    }
    static boxContainsBox(b0,b1){
        return b0[0] <= b1[0] && b0[3] >= b1[3] &&
            b0[1] <= b1[1] && b0[4] >= b1[4] &&
            b0[2] <= b1[2] && b0[5] >= b1[5]
    }
    static boxUnionBox(b1,b2,out){
        out[0] = b1[0] < b2[0] ? b1[0] : b2[0];out[1] = b1[1] < b2[1] ? b1[1] : b2[1];out[2] = b1[2] < b2[2] ? b1[2] : b2[2];
        out[3] = b1[3] > b2[3] ? b1[3] : b2[3];out[4] = b1[4] > b2[4] ? b1[4] : b2[4];out[5] = b1[5] > b2[5] ? b1[5] : b2[5];
        return out;
    }
    static crossVectors(x0,y0,z0,x1,y1,z1,out=null,start=0){
        let o=out||new Float64Array(3);
        o[start] = y0 * z1 - z0 * y1;
        o[start+1] = z0 * x1 - x0 * z1;
        o[start+2] = x0 * y1 - y0 * x1;
        return o;
    }
    static convertMat3(input,out=new Mat3()){
        let es=out.elements;
        let ie=DEFAULT_33;
        if(Method.isArray(input)){
            es[0]=input[0];es[1]=input[1];es[2]=input[2];
            es[3]=input[3];es[4]=input[4];es[5]=input[5];
            es[6]=input[6];es[7]=input[7];es[8]=input[8];
            return out;
        }
        if(input instanceof Mat3){
            Method.copyElements(input,out);
            return out;
        }
        if(typeof input === 'object'){
            ie=input.elements;
            /**
             * assume input elements in column-major
             */
            es[0]=ie[0];es[1]=ie[3];es[2]=ie[6];
            es[3]=ie[1];es[4]=ie[4];es[5]=ie[7];
            es[6]=ie[2];es[7]=ie[5];es[8]=ie[8];
            return out;
        }
        return out;
    }
    static convertQuat(input,out=new Quat()){
        let es=out.elements;
        if(Method.isArray(input)){
            es[0]=input[0];es[1]=input[1];es[2]=input[2];es[3]=input[3];
            return out;
        }
        if(typeof input === 'object'){
            es[0]=input.x;es[1]=input.y;es[2]=input.z;es[3]=input.w;
            return out;
        }
        return out;
    }
    static convertVec3(input,out=new Vec3()){
        let es=out.elements;
        if(Method.isArray(input)){
            es[0]=input[0];es[1]=input[1];es[2]=input[2];
            return out;
        }
        if(typeof input === 'object'){
            es[0]=input.x;es[1]=input.y;es[2]=input.z;
            return out;
        }
        return out;
    }
    static combineMat3Vec3ToTransform(vec3,mat3,transform){
        let m=mat3.elements,v=vec3.elements,t=transform.elements;
        t[0]=v[0];t[1]=v[1];t[2]=v[2];
        t[3]=m[0];t[4]=m[1];t[5]=m[2];
        t[6]=m[3];t[7]=m[4];t[8]=m[5];
        t[9]=m[6];t[10]=m[7];t[11]=m[8];
        return transform;
    }
    static copyTransform(_src,dst=null){
        let src=Method.isArray(_src)?_src:_src.elements;
        let _dst=dst||new Transform();
        Method.copyElements(src,_dst.elements);
        return _dst;
    }
    static copyMat3(_src,dst=null){
        let src=Method.isArray(_src)?_src:_src.elements;
        let _dst=dst||new Mat3();
        Method.copyElements(src,_dst.elements);
        return _dst;
    }
    static copyAabb(_src,dst=null){
        let src=Method.isArray(_src)?_src:_src.elements;
        let _dst=dst||new Aabb();
        Method.copyElements(src,_dst.elements);
        return _dst;
    }
    static copyVec3(_src,dst=null){
        let src=Method.isArray(_src)?_src:_src.elements;
        let _dst=dst||new Vec3();
        Method.copyElements(src,_dst.elements);
        return _dst;
    }
    static copyQuat(_src,dst=null){
        let src=Method.isArray(_src)?_src:_src.elements;
        let _dst=dst||new Quat();
        Method.copyElements(src,_dst.elements);
        return _dst;
    }
    static copyElements(src,dst,startSrc=0,startDst=0,length=0){
        let ss=startSrc||0;
        let sd=startDst||0;
        let len=length||Math.min(src.length,dst.length);
        for(let i=0;i<len;i++){
            dst[i+sd]=src[i+ss];
        }
        return dst;
    }
    static extractQuatFromTransform(transform,out){
        let tq=Method.isArray(out)?out:out.elements,tf=Method.isArray(transform)?transform:transform.elements;
        let t = tf[3] + tf[7] + tf[11];
        let s;
        if(t > 0) {
            s = Math.sqrt(t + 1);
            tq[3] = 0.5 * s;
            s = 0.5 / s;
            tq[0] = (tf[10] - tf[8]) * s;tq[1] = (tf[5] - tf[9]) * s;tq[2] = (tf[6] - tf[4]) * s;
        } else if(tf[3] > tf[7]) {
            if(tf[3] > tf[11]) {
                s = Math.sqrt(tf[3] - tf[7] - tf[11] + 1);
                tq[0] = 0.5 * s;
                s = 0.5 / s;
                tq[1] = (tf[4] + tf[6]) * s;tq[2] = (tf[5] + tf[9]) * s;tq[3] = (tf[10] - tf[8]) * s;
            } else {
                s = Math.sqrt(tf[11] - tf[3] - tf[7] + 1);
                tq[2] = 0.5 * s;
                s = 0.5 / s;
                tq[0] = (tf[5] + tf[9]) * s;tq[1] = (tf[8] + tf[10]) * s;tq[3] = (tf[6] - tf[4]) * s;
            }
        } else if(tf[7] > tf[11]) {
            s = Math.sqrt(tf[7] - tf[11] - tf[3] + 1);
            tq[1] = 0.5 * s;
            s = 0.5 / s;
            tq[0] = (tf[4] + tf[6]) * s;tq[2] = (tf[8] + tf[10]) * s;tq[3] = (tf[5] - tf[9]) * s;
        } else {
            s = Math.sqrt(tf[11] - tf[3] - tf[7] + 1);
            tq[2] = 0.5 * s;
            s = 0.5 / s;
            tq[0] = (tf[5] + tf[9]) * s;tq[1] = (tf[8] + tf[10]) * s;tq[3] = (tf[6] - tf[4]) * s;
        }
    }
    static extractVec3FromTransform(transform,out){
        let vc=Method.isArray(out)?out:out.elements,tf=Method.isArray(transform)?transform:transform.elements;
        vc[0]=tf[0];vc[1]=tf[1];vc[2]=tf[2];
    }
    static expandBoxByScale(box,scale){
        box[0] -= scale;box[1] -= scale;box[2] -= scale;
        box[3] += scale;box[4] += scale;box[5] += scale;
    }
    static expandBoxByPoint(box,x,y,z){
        let addToMinX = x>0 ? 0 : x,addToMinY = y>0 ? 0 : y,addToMinZ = z>0 ? 0 : z;
        let addToMaxX = x<0 ? 0 : x,addToMaxY = y<0 ? 0 : y,addToMaxZ = z<0 ? 0 : z;
        box[0] += addToMinX;box[1] += addToMinY;box[2] += addToMinZ;
        box[3] += addToMaxX;box[4] += addToMaxY;box[5] += addToMaxZ;
    }
    static fillValue(tar,start,end,value){
        let es=Method.isArray(tar)?tar:tar.elements;
        for(let i=start;i<es.length;i++){
            if(i>end)break;
            es[i]=value;
        }
        return tar;
    }
    static inverseInertia(axisX,axisY,axisZ,rv,ra,invMass,mass){
        let invI1 = rv[0] * axisX + rv[1] * axisY + rv[2] * axisZ;
        if(invI1 > 0) {
            let dot = axisX * ra[0] + axisY * ra[1] + axisZ * ra[2];
            let projsq = ra[0] * ra[0] + ra[1] * ra[1] + ra[2] * ra[2] - dot * dot;
            if(projsq > 0) {
                if(invMass > 0) {
                    invI1 = 1 / (1 / invI1 + mass * projsq);
                } else {
                    invI1 = 0;
                }
            }
        }
        return invI1;
    }
    static inverseTransformM3(tf,wb,out=null){
        let tf1 = Method.isArray(tf)?tf:tf.elements;
        let wb1 = Method.isArray(wb)?wb:wb.elements;
        let o=out||new Float64Array(9);
        o[0] = tf1[3] * wb1[0] + tf1[6] * wb1[3] + tf1[9] * wb1[6];
        o[1] = tf1[3] * wb1[1] + tf1[6] * wb1[4] + tf1[9] * wb1[7];
        o[2] = tf1[3] * wb1[2] + tf1[6] * wb1[5] + tf1[9] * wb1[8];
        o[3] = tf1[4] * wb1[0] + tf1[7] * wb1[3] + tf1[10] * wb1[6];
        o[4] = tf1[4] * wb1[1] + tf1[7] * wb1[4] + tf1[10] * wb1[7];
        o[5] = tf1[4] * wb1[2] + tf1[7] * wb1[5] + tf1[10] * wb1[8];
        o[6] = tf1[5] * wb1[0] + tf1[8] * wb1[3] + tf1[11] * wb1[6];
        o[7] = tf1[5] * wb1[1] + tf1[8] * wb1[4] + tf1[11] * wb1[7];
        o[8] = tf1[5] * wb1[2] + tf1[8] * wb1[5] + tf1[11] * wb1[8];
        return o;
    }
    static isArray(obj){
        if(typeof obj !== 'object')return false;
        if(Array.isArray(obj))return true;
        return obj[0]!==undefined;
    }
    static inverseRotateVec3(_v,_rot){
        let v=Method.isArray(_v)?_v:_v.elements,rot=Method.isArray(_rot)?_rot:_rot.elements;
        let __tmp__X = rot[0] * v[0] + rot[3] * v[1] + rot[6] * v[2];
        let __tmp__Y = rot[1] * v[0] + rot[4] * v[1] + rot[7] * v[2];
        let __tmp__Z = rot[2] * v[0] + rot[5] * v[1] + rot[8] * v[2];
        v[0] = __tmp__X;v[1] = __tmp__Y;v[2] = __tmp__Z;
    }
    static inverseTransformVec3(_tf,vec3,op=0,out=new Vec3(),vecStart=0,outStart=0){
        /**
         * op:
         *      0:translate and rotate;
         *      1:only rotate;
         */
        let v=Method.isArray(vec3)?vec3:vec3.elements,tf=Method.isArray(_tf)?_tf:_tf.elements;
        let vX = v[vecStart],vY = v[vecStart+1],vZ = v[vecStart+2];
        if(op===0){
            vX -= tf[0];vY -= tf[1];vZ -= tf[2];
        }
        let __tmp__X = tf[3] * vX + tf[6] * vY + tf[9] * vZ;
        let __tmp__Y = tf[4] * vX + tf[7] * vY + tf[10] * vZ;
        let __tmp__Z = tf[5] * vX + tf[8] * vY + tf[11] * vZ;
        vX = __tmp__X;
        vY = __tmp__Y;
        vZ = __tmp__Z;
        let es=Method.isArray(out)?out:out.elements;
        es[outStart] = vX;es[outStart+1] = vY;es[outStart+2] = vZ;
        return out;
    }
    static multiplyTransform(_src,_m,_dst){
        let src=Method.isArray(_src)?_src:_src.elements;
        let m=Method.isArray(_m)?_m:_m.elements;
        let dst=Method.isArray(_dst)?_dst:_dst.elements;
        dst[3] = m[3] * src[3] + m[4] * src[6] + m[5] * src[9];
        dst[4] = m[3] * src[4] + m[4] * src[7] + m[5] * src[10];
        dst[5] = m[3] * src[5] + m[4] * src[8] + m[5] * src[11];
        dst[6] = m[6] * src[3] + m[7] * src[6] + m[8] * src[9];
        dst[7] = m[6] * src[4] + m[7] * src[7] + m[8] * src[10];
        dst[8] = m[6] * src[5] + m[7] * src[8] + m[8] * src[11];
        dst[9] = m[9] * src[3] + m[10] * src[6] + m[11] * src[9];
        dst[10] = m[9] * src[4] + m[10] * src[7] + m[11] * src[10];
        dst[11] = m[9] * src[5] + m[10] * src[8] + m[11] * src[11];
        dst[0] = m[3] * src[0] + m[4] * src[1] + m[5] * src[2];
        dst[1] = m[6] * src[0] + m[7] * src[1] + m[8] * src[2];
        dst[2] = m[9] * src[0] + m[10] * src[1] + m[11] * src[2];
        dst[0] += m[0];
        dst[1] += m[1];
        dst[2] += m[2];
    }
    static mat3ToQuat(m3,out){
        let tm=Method.isArray(m3)?m3:m3.elements;
        let oe=Method.isArray(out)?out:out.elements;
        let relRot00 = tm[0],relRot01 = tm[1],relRot02 = tm[2];
        let relRot10 = tm[3],relRot11 = tm[4],relRot12 = tm[5];
        let relRot20 = tm[6],relRot21 = tm[7],relRot22 = tm[8];
        let relQX, relQY, relQZ, relQW;
        let t = relRot00 + relRot11 + relRot22;
        let s;
        if(t > 0) {
            s = Math.sqrt(t + 1);
            relQW = 0.5 * s;
            s = 0.5 / s;
            relQX = (relRot21 - relRot12) * s;
            relQY = (relRot02 - relRot20) * s;
            relQZ = (relRot10 - relRot01) * s;
        } else if(relRot00 > relRot11) {
            if(relRot00 > relRot22) {
                s = Math.sqrt(relRot00 - relRot11 - relRot22 + 1);
                relQX = 0.5 * s;
                s = 0.5 / s;
                relQY = (relRot01 + relRot10) * s;
                relQZ = (relRot02 + relRot20) * s;
                relQW = (relRot21 - relRot12) * s;
            } else {
                s = Math.sqrt(relRot22 - relRot00 - relRot11 + 1);
                relQZ = 0.5 * s;
                s = 0.5 / s;
                relQX = (relRot02 + relRot20) * s;
                relQY = (relRot12 + relRot21) * s;
                relQW = (relRot10 - relRot01) * s;
            }
        } else if(relRot11 > relRot22) {
            s = Math.sqrt(relRot11 - relRot22 - relRot00 + 1);
            relQY = 0.5 * s;
            s = 0.5 / s;
            relQX = (relRot01 + relRot10) * s;
            relQZ = (relRot12 + relRot21) * s;
            relQW = (relRot02 - relRot20) * s;
        } else {
            s = Math.sqrt(relRot22 - relRot00 - relRot11 + 1);
            relQZ = 0.5 * s;
            s = 0.5 / s;
            relQX = (relRot02 + relRot20) * s;
            relQY = (relRot12 + relRot21) * s;
            relQW = (relRot10 - relRot01) * s;
        }
        oe[0]=relQX;oe[1]=relQY;oe[2]=relQZ;oe[3]=relQW;
        return out;
    }
    static mat3ToVec3(bs,out=null){
        let ot=out||new Float64Array(3),tm=Method.isArray(bs)?bs:bs.elements;
        let relRot00 = tm[0];
        let relRot01 = tm[1];
        let relRot02 = tm[2];
        let relRot11 = tm[4];
        let relRot12 = tm[5];
        let relRot21 = tm[7];
        let relRot22 = tm[8];
        let sy = relRot02;
        if(sy <= -1) {
            let xSubZ = Math.atan2(relRot21,relRot11);
            ot[0] = xSubZ * 0.5;
            ot[1] = -1.570796326794895;
            ot[2] = -xSubZ * 0.5;
        } else if(sy >= 1) {
            let xAddZ = Math.atan2(relRot21,relRot11);
            ot[0] = xAddZ * 0.5;
            ot[1] = 1.570796326794895;
            ot[2] = xAddZ * 0.5;
        } else {
            ot[0] = Math.atan2(-relRot12,relRot22);
            ot[1] = Math.asin(sy);
            ot[2] = Math.atan2(-relRot01,relRot00);
        }
        return ot;
    }
    static makeBasis(x0,y0,z0,x1,y1,z1,outElements){
        let ax=outElements;
        let aaXX = x0,aaXY = y0,aaXZ = z0;
        let aaZX = x1,aaZY = y1,aaZZ = z1;
        let aaYX = aaZY * aaXZ - aaZZ * aaXY,aaYY = aaZZ * aaXX - aaZX * aaXZ,aaYZ = aaZX * aaXY - aaZY * aaXX;
        ax[0] = aaYY * aaZZ - aaYZ * aaZY;
        ax[1] = aaYZ * aaZX - aaYX * aaZZ;
        ax[2] = aaYX * aaZY - aaYY * aaZX;
        ax[3] = aaYX;
        ax[4] = aaYY;
        ax[5] = aaYZ;
        ax[6] = aaXY * aaYZ - aaXZ * aaYY;
        ax[7] = aaXZ * aaYX - aaXX * aaYZ;
        ax[8] = aaXX * aaYY - aaXY * aaYX;
        let l = ax[0] * ax[0] + ax[1] * ax[1] + ax[2] * ax[2];
        if(l > 0) l = 1 / Math.sqrt(l);
        ax[0] *= l;ax[1] *= l;ax[2] *= l;
        l = ax[3] * ax[3] + ax[4] * ax[4] + ax[5] * ax[5];
        if(l > 0) l = 1 / Math.sqrt(l);
        ax[3] *= l;ax[4] *= l;ax[5] *= l;
        l = ax[6] * ax[6] + ax[7] * ax[7] + ax[8] * ax[8];
        if(l > 0) l = 1 / Math.sqrt(l);
        ax[6] *= l;ax[7] *= l;ax[8] *= l;
    }
    static multiplyBasis(b0,b1,out){
        let b2=out||new Float64Array(9);
        b2[0] = b0[0] * b1[0] + b0[1] * b1[1] + b0[2] * b1[2];
        b2[1] = b0[3] * b1[0] + b0[4] * b1[1] + b0[5] * b1[2];
        b2[2] = b0[6] * b1[0] + b0[7] * b1[1] + b0[8] * b1[2];
        b2[3] = b0[0] * b1[3] + b0[1] * b1[4] + b0[2] * b1[5];
        b2[4] = b0[3] * b1[3] + b0[4] * b1[4] + b0[5] * b1[5];
        b2[5] = b0[6] * b1[3] + b0[7] * b1[4] + b0[8] * b1[5];
        b2[6] = b0[0] * b1[6] + b0[1] * b1[7] + b0[2] * b1[8];
        b2[7] = b0[3] * b1[6] + b0[4] * b1[7] + b0[5] * b1[8];
        b2[8] = b0[6] * b1[6] + b0[7] * b1[7] + b0[8] * b1[8];
        return b2;
    }
    static multiplyArray(a,b,startA=0,startB=0,length=3,out=null,outStart=0){
        let sa=startA||0;
        let sb=startB||0;
        let so=outStart||0;
        let s=0;
        let len=length||Math.min(a.length,b.length);
        for(let i=0;i<len;i++){
            let t=a[i+sa]*b[i+sb];
            if(out)out[i+so]=t;
            s+=t;
        }
        return s;
    }
    static normalize(ary,start,length,scale=1){
        let l=0;
        let end=start+length;
        for(let i=start;i<end;i++){
            l+=ary[i]*ary[i];
        }
        if(l>0)l=1/Math.sqrt(l);
        for(let i=start;i<end;i++){
            ary[i]*=l*scale;
        }
        return ary;
    }
    static quatToMat3(x,y,z,w,out=null){
        let o=out||new Float64Array(9);
        let x2 = 2 * x;
        let y2 = 2 * y;
        let z2 = 2 * z;
        let xx = x * x2;
        let yy = y * y2;
        let zz = z * z2;
        let xy = x * y2;
        let yz = y * z2;
        let xz = x * z2;
        let wx = w * x2;
        let wy = w * y2;
        let wz = w * z2;
        o[0] = 1 - yy - zz;
        o[1] = xy - wz;
        o[2] = xz + wy;
        o[3] = xy + wz;
        o[4] = 1 - xx - zz;
        o[5] = yz - wx;
        o[6] = xz - wy;
        o[7] = yz + wx;
        o[8] = 1 - xx - yy;
        return o;
    }
    static rotateTransform(_tf,_rot){
        let tf=Method.isArray(_tf)?_tf:_tf.elements,rot=Method.isArray(_rot)?_rot:_rot.elements;
        let __tmp__00 = rot[0] * tf[3] + rot[1] * tf[6] + rot[2] * tf[9];
        let __tmp__01 = rot[0] * tf[4] + rot[1] * tf[7] + rot[2] * tf[10];
        let __tmp__02 = rot[0] * tf[5] + rot[1] * tf[8] + rot[2] * tf[11];
        let __tmp__10 = rot[3] * tf[3] + rot[4] * tf[6] + rot[5] * tf[9];
        let __tmp__11 = rot[3] * tf[4] + rot[4] * tf[7] + rot[5] * tf[10];
        let __tmp__12 = rot[3] * tf[5] + rot[4] * tf[8] + rot[5] * tf[11];
        let __tmp__20 = rot[6] * tf[3] + rot[7] * tf[6] + rot[8] * tf[9];
        let __tmp__21 = rot[6] * tf[4] + rot[7] * tf[7] + rot[8] * tf[10];
        let __tmp__22 = rot[6] * tf[5] + rot[7] * tf[8] + rot[8] * tf[11];
        tf[3] = __tmp__00;
        tf[4] = __tmp__01;
        tf[5] = __tmp__02;
        tf[6] = __tmp__10;
        tf[7] = __tmp__11;
        tf[8] = __tmp__12;
        tf[9] = __tmp__20;
        tf[10] = __tmp__21;
        tf[11] = __tmp__22;
    }
    static rotateVec3(_v,_rot){
        let v=Method.isArray(_v)?_v:_v.elements,rot=Method.isArray(_rot)?_rot:_rot.elements;
        let __tmp__X = rot[0] * v[0] + rot[1] * v[1] + rot[2] * v[2];
        let __tmp__Y = rot[3] * v[0] + rot[4] * v[1] + rot[5] * v[2];
        let __tmp__Z = rot[6] * v[0] + rot[7] * v[1] + rot[8] * v[2];
        v[0] = __tmp__X;v[1] = __tmp__Y;v[2] = __tmp__Z;
    }
    static rotateVecTo(x,y,z,rot,out=null){
        let o=out||new Float64Array(3);
        let rx = rot[0] * x + rot[1] * y + rot[2] * z;
        let ry = rot[3] * x + rot[4] * y + rot[5] * z;
        let rz = rot[6] * x + rot[7] * y + rot[8] * z;
        o[0]=rx;o[1]=ry;o[2]=rz;
        return o;
    }
    static setXYZ(obj,x,y,z,start=0){
        if(Method.isArray(obj)){
            obj[start]=x;obj[start+1]=y;obj[start+2]=z;
        }else{
            obj.x=x;obj.y=y;obj.z=z;
        }
        return obj;
    }
    static setXYZW(obj,x,y,z,w,start=0){
        if(Method.isArray(obj)){
            obj[start]=x;obj[start+1]=y;obj[start+2]=z;obj[start+3]=w;
        }else{
            obj.x=x;obj.y=y;obj.z=z;obj.w=w;
        }
    }
    static setIncidentVertex(obj,x,y,wx,wy,wz,start=0){
        if(Method.isArray(obj)){
            obj[start]=x;
            obj[start+1]=y;
            obj[start+2]=wx;
            obj[start+3]=wy;
            obj[start+4]=wz;
        }else{
            obj.x=x;
            obj.y=y;
            obj.wx=wx;
            obj.wy=wy;
            obj.wz=wz;
        }
    }
    static setM3X3(obj,e00,e01,e02,e10,e11,e12,e20,e21,e22,start=0){
        if(Method.isArray(obj)){
            obj[start]=e00;obj[start+1]=e01;obj[start+2]=e02;
            obj[start+3]=e10;obj[start+4]=e11;obj[start+5]=e12;
            obj[start+6]=e20;obj[start+7]=e21;obj[start+8]=e22;
        }else if(obj instanceof Mat3){
            let es=obj.elements;
            es[0]=e00;es[1]=e01;es[2]=e02;
            es[3]=e10;es[4]=e11;es[5]=e12;
            es[6]=e20;es[7]=e21;es[8]=e22;
        }else{
            /**
             * THREE.Matrix3 like
             */
            obj.set(e00,e01,e02,e10,e11,e12,e20,e21,e22);
        }
    }
    static setTransformRotation(tf,rt){
        let t=Method.isArray(tf)?tf:tf.elements,r=Method.isArray(rt)?rt:rt.elements;
        t[3]=r[0];t[4]=r[1];t[5]=r[2];
        t[6]=r[3];t[7]=r[4];t[8]=r[5];
        t[9]=r[6];t[10]=r[7];t[11]=r[8];
    }
    static setTransformOrientation(_tf,_quat){
        let tf=Method.isArray(_tf)?_tf:_tf.elements,qt=Method.isArray(_quat)?_quat:_quat.elements;
        let x = qt[0],y = qt[1],z = qt[2],w = qt[3];
        Method.quatToMat3(x,y,z,w,tf0);
        tf[3] = tf0[0];
        tf[4] = tf0[1];
        tf[5] = tf0[2];
        tf[6] = tf0[3];
        tf[7] = tf0[4];
        tf[8] = tf0[5];
        tf[9] = tf0[6];
        tf[10] = tf0[7];
        tf[11] = tf0[8];
    }
    static subArray(a,b,out,startA=0,startB=0,startOut=0,length=3){
        let sa=startA||0;
        let sb=startB||0;
        let so=startOut||0;
        let len=length||Math.min(a.length,b.length);
        for(let i=0;i<len;i++){
            out[i+so]=a[i+sa]-b[i+sb];
        }
        return out;
    }
    static setJacobian(basisX,basisY,basisZ,vec1Elements,vec2Elements,outElements){
        let j = outElements,vec1=vec1Elements,vec2=vec2Elements;
        j[0] = basisX;
        j[1] = basisY;
        j[2] = basisZ;
        j[3] = basisX;
        j[4] = basisY;
        j[5] = basisZ;
        j[6] = vec1[1] * basisZ - vec1[2] * basisY;
        j[7] = vec1[2] * basisX - vec1[0] * basisZ;
        j[8] = vec1[0] * basisY - vec1[1] * basisX;
        j[9] = vec2[1] * basisZ - vec2[2] * basisY;
        j[10] = vec2[2] * basisX - vec2[0] * basisZ;
        j[11] = vec2[0] * basisY - vec2[1] * basisX;
    }
    static setJacobianElements(jab,l1x,l1y,l1z,l2x,l2y,l2z,a1x,a1y,a1z,a2x,a2y,a2z){
        let j=Method.isArray(jab)?jab:jab.elements;
        j[0]=l1x;j[1]=l1y;j[2]=l1z;
        j[3]=l2x;j[4]=l2y;j[5]=l2z;
        j[6]=a1x;j[7]=a1y;j[8]=a1z;
        j[9]=a2x;j[10]=a2y;j[11]=a2z;
        return jab;
    }
    static scaleArray(a,s,out,startA=0,startOut=0,length=3){
        let sa=startA||0;
        let so=startOut||0;
        let len=length||a.length;
        for(let i=0;i<len;i++){
            out[i+so]=a[i+sa]*s;
        }
        return out;
    }
    static setRotFromTwoVec3(axis1X,axis1Y,axis1Z,axis2X,axis2Y,axis2Z,outQuat=null,outMat3=null){
        let ot=outMat3||new Float64Array(9);
        let oq=outQuat||new Float64Array(4);
        let d = axis1X * axis2X + axis1Y * axis2Y + axis1Z * axis2Z;
        if(d < -0.999999999) {
            Method.vecToQuat(axis1X,axis1Y,axis1Z,tv0);
        } else {
            Method.crossVectors(axis1X,axis1Y,axis1Z,axis2X,axis2Y,axis2Z,tv0);
            let w = Math.sqrt((1 + d) * 0.5);
            d = 0.5 / w;
            Method.scaleArray(tv0,d,tv0,0,0,3);
            tv0[3]=w;
        }
        Method.setXYZW(oq,tv0[0],tv0[1],tv0[2],tv0[3]);
        Method.quatToMat3(tv0[0],tv0[1],tv0[2],tv0[3],ot);
    }
    static setBox(x1,y1,z1,x2,y2,z2,out=null){
        let ot=out||new Float64Array(6);
        ot[0]=x1<x2?x1:x2;ot[1]=y1<y2?y1:y2;ot[2]=z1<z2?z1:z2;
        ot[3]=x1<x2?x2:x1;ot[4]=y1<y2?y2:y1;ot[5]=z1<z2?z2:z1;
        return out;
    }
    static transformVec3(_tf,vec3,op=0,out=new Vec3(),vecStart=0,outStart=0){
        /**
         * op:
         *      0:translate and rotate;
         *      1:only rotate;
         *      2:only translate
         */
        let v=Method.isArray(vec3)?vec3:vec3.elements,tf=Method.isArray(_tf)?_tf:_tf.elements;
        let vX=v[vecStart],vY=v[vecStart+1],vZ=v[vecStart+2];
        if(op!==2){
            vX = tf[3] * v[0] + tf[4] * v[1] + tf[5] * v[2];
            vY = tf[6] * v[0] + tf[7] * v[1] + tf[8] * v[2];
            vZ = tf[9] * v[0] + tf[10] * v[1] + tf[11] * v[2];
        }
        if(op!==1){
            vX += tf[0];vY += tf[1];vZ += tf[2];
        }
        let es=Method.isArray(out)?out:out.elements;
        es[outStart] = vX;es[outStart+1] = vY;es[outStart+2] = vZ;
        return out;
    }
    static transformM3(tf,lb,out=null){
        //transform
        let tf1 = Method.isArray(tf)?tf:tf.elements;
        let bs1 = Method.isArray(lb)?lb:lb.elements;
        let b1=out||new Float64Array(9);
        b1[0] = tf1[3] * bs1[0] + tf1[4] * bs1[1] + tf1[5] * bs1[2];
        b1[1] = tf1[6] * bs1[0] + tf1[7] * bs1[1] + tf1[8] * bs1[2];
        b1[2] = tf1[9] * bs1[0] + tf1[10] * bs1[1] + tf1[11] * bs1[2];
        b1[3] = tf1[3] * bs1[3] + tf1[4] * bs1[4] + tf1[5] * bs1[5];
        b1[4] = tf1[6] * bs1[3] + tf1[7] * bs1[4] + tf1[8] * bs1[5];
        b1[5] = tf1[9] * bs1[3] + tf1[10] * bs1[4] + tf1[11] * bs1[5];
        b1[6] = tf1[3] * bs1[6] + tf1[4] * bs1[7] + tf1[5] * bs1[8];
        b1[7] = tf1[6] * bs1[6] + tf1[7] * bs1[7] + tf1[8] * bs1[8];
        b1[8] = tf1[9] * bs1[6] + tf1[10] * bs1[7] + tf1[11] * bs1[8];
    }
    static transposeM33(src,dst){
        let s=Method.isArray(src)?src:src.elements;
        let d=Method.isArray(dst)?dst:dst.elements;
        d[0]=s[0];
        d[1]=s[3];
        d[2]=s[6];
        d[3]=s[1];
        d[4]=s[4];
        d[5]=s[7];
        d[6]=s[2];
        d[7]=s[5];
        d[8]=s[8];
        return dst;
    }
    static vecToQuat(x,y,z,out=null){
        let o=out||new Float64Array(4);
        let vX, vY, vZ;
        let x2 = x * x, y2 = y * y, z2 = z * z;
        let d;
        if(x2 < y2) {
            if(x2 < z2) {
                d = 1 / Math.sqrt(y2 + z2);
                vX = 0;vY = z * d;vZ = -y * d;
            } else {
                d = 1 / Math.sqrt(x2 + y2);
                vX = y * d;vY = -x * d;vZ = 0;
            }
        } else if(y2 < z2) {
            d = 1 / Math.sqrt(z2 + x2);
            vX = -z * d;vY = 0;vZ = x * d;
        } else {
            d = 1 / Math.sqrt(x2 + y2);
            vX = y * d;vY = -x * d;vZ = 0;
        }
        o[0] = vX;o[1] = vY;o[2] = vZ;o[3] = 0;
        return o;
    }
}